/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <boost/scoped_ptr.hpp>

#include "Texture.h"
#include <SharedRep.h>
#include <AutoContainer.h>
#include <vector>
#include <filesystem/String.hpp>
#include <Atom.h>
#include <HardwareDependentObject.h>
#include <pictureformat/Loader.h>
#include <lib3d/hard/Hardware.h>
#include <lib3d/hard/Manager.h>
#include <CompilerFeatures.h>

namespace liba {
namespace lib3d {
namespace material {

//class to hold information about texture sequence
class TextureSequenceName
{
public:
	TextureSequenceName()
	:	wrap( true ),
		frame_start(0),
		frame_end(0),
		total_frames_count(0)
	{}
	bool operator<( const TextureSequenceName & na )const;

	filesystem::String image;
	int frame_start;
	int frame_end;
	int total_frames_count;
	bool wrap;
};

class TextureSequenceFrames;

//shared representation of the particular frame
class TextureSequenceFrameRep : public HardwareDependentObject<hard::Manager, hard::Texture>, public SharedPrecachedRepBase
{
public:
	class Name
	{
	public:
		Name(TextureSequenceFrames * parent, const filesystem::String & image, int frame_number, int total_frames_count)
		:	parent(parent),
			frame_number(frame_number),
			total_frames_count(total_frames_count),
			image(image),
			wrap(true)
		{}

		bool operator<( const Name & na )const;

		filesystem::String image;
		int frame_number;
		int total_frames_count;
		bool wrap;
		TextureSequenceFrames * parent;
	};

	TextureSequenceFrameRep(const Name & name);
	~TextureSequenceFrameRep();

	void load(const Name & name);
	void render_to_list( hard::Hardware * hardware, hard::RenderTexture * render_texture );
protected:
	virtual void on_add_hardware(hard::Manager::hardware_type * ha) LIBA_OVERRIDE;
private:
	pictureformat::Loader * pl; //shared loader between frames
	bool alpha_channel;
};

//class that holds shared rep of the frame
class TextureSequenceFrame : public SharedPrecached<TextureSequenceFrameRep::Name, TextureSequenceFrameRep>
{
public:
	TextureSequenceFrame(const TextureSequenceFrameRep::Name & name)
	{
		load_rep(name);
	}

	void render_to_list( hard::Hardware * hardware, hard::RenderTexture * render_texture )
	{
		return get_rep()->render_to_list( hardware, render_texture );
	}
};

//shared rep of the whole sequence
class TextureSequenceFrames : public AutoContainer< std::vector<TextureSequenceFrame *> >, public SharedPrecachedRepBase
{
public:
	TextureSequenceFrames(const TextureSequenceName & name);
	
	void load(const TextureSequenceName & name);

	void render_to_list(unsigned frame_number, hard::Hardware * hardware, hard::RenderTexture * render_texture)
	{
		return operator[](frame_number)->render_to_list( hardware, render_texture );
	}

	pictureformat::Loader * get_loader(const filesystem::String & image);
	void release_loader();

private:
	boost::scoped_ptr<pictureformat::Loader> pl; //shared loader between frames
	int loader_ref_count;
};

class TextureSequence : public Texture, public SharedPrecached<TextureSequenceName, TextureSequenceFrames>
{
public:
	typedef name_type Name;

	TextureSequence(const Atom & animate_node_name, const TextureSequenceName & name);
	virtual TextureSequence * clone()const;

	virtual void render_to_list( hard::Hardware * hardware, hard::RenderTexture * render_texture )
	{
		if(current_frame >= get_frames_count())
			return;

		Texture::render_to_list(hardware, render_texture);
		return get_rep()->render_to_list(current_frame, hardware, render_texture);
	}

	virtual bool ChangeAttribute(const Atom & name, const filesystem::String & value){ return false; };
	virtual bool GetAttribute(const Atom & name, filesystem::String * value)const{ return false; };

	size_t get_frames_count()const
	{
		return get_rep()->size();
	}
	void change_frame(unsigned frame)
	{
		current_frame = frame;
	}

private:
	size_t current_frame;
};

} // namespace material
} // namespace lib3d
} // namespace liba
using namespace liba;

